<?php
//******************************************************************
class engine {
	public $primaryModule = array('module'=>null, 'method'=>null);
	public $sql = null;
	public $user = null;
	public $xml = null;
	public $theme = null;
	public $language = null;
	protected $urlParams = array();
	protected $templatesList = array();
	protected $urlAlias = null;
	
	public function __construct() {
		$this->sql = new sql();
		$this->user = new user($this);

		$this->theme = $this->user->getPreferredTheme();
		$this->language = $this->user->getPreferredLanguage();
		require_once (ROOT_PATH."config/theme.".$this->theme.".".$this->language.".php");
		$this->setLocale();

		$this->getModuleByURL($_SERVER['REQUEST_URI']);
		$this->initXML();
		try {
			$this->moduleRun($this->primaryModule['module'], $this->primaryModule['method'], array(), true);
		} catch (Exception $e) {
			if ($e->getCode() == 404) $this->jump("/");
			else throw $e;
		}
		$this->output();
	}
	
	private function initXML() {
		$this->xml = new extendedSimpleXML('<?xml version="1.0" encoding="UTF-8"?><engine/>');
		$this->xml['cssURL'] = CSS_URL;
		$this->xml['jsURL'] = JS_URL;
		$this->xml['imagesURL'] = IMAGES_URL;
		$this->xml['currentTheme'] = $this->theme;
		$this->xml['currentLanguage'] = $this->language;
		$this->xml['currentUserId'] = $this->user->id;
		$this->xml['siteName'] = SITE_NAME;
	}
	
	private function setLocale() {
		putenv ("LC_ALL=".LOCALE);
		setlocale (LC_ALL, LOCALE);
		bindtextdomain("messages", ROOT_PATH."locale");
		textdomain("messages");
		bind_textdomain_codeset("messages", 'UTF-8');
	}
	
	public function getRealURL($URL) {
		if (isset($GLOBALS['urlAliases']['absolute'][$URL])) {
			return $GLOBALS['urlAliases']['absolute'][$URL];
		}

		foreach ($GLOBALS['urlAliases']['partial'] as $k => $v) {
			if (strpos($URL, $k) !== false) {
				$this->urlAlias = $k;
				return str_replace($k, $v, $URL);
			}
		}

		return $URL;
	}
	
	public function getModuleByURL($url) {
		$result = array('module'=>null, 'method'=>null, 'params'=>array());
		$d = array();
		$parsedURL = parse_url($url);
		$parsedURL['path'] = $this->getRealURL($parsedURL['path']);
		foreach (explode("/", $parsedURL['path']) as $v) {
			$v = trim($v);
			if ($v != "")	{
				$d[] = $v;
			}
		}
		if (count($d)==1) {
			$result['module'] = $d[0];
		} elseif (count($d)>=2) {
			$result['module'] = $d[0];
			$result['method'] = $d[1];
			if (count($d)>2) {
				for ($i=2; $i<count($d); $i++) {
					$result['params'][] = $d[$i];
				}
			}
		}
		
		$this->primaryModule['module'] = $result['module'];
		$this->primaryModule['method'] = $result['method'];
		$this->urlParams = $result['params'];
	}
	
	public function getUrlParam($item=null) {
		if ($item === null) {
			return $this->urlParams;
		} else {
			if (isset($this->urlParams[$item])) {
				return $this->urlParams[$item];
			} else {
				return null;
			}
		}
	}
	
	public function moduleRun($module=null, $method=null, $params=array(), $isPrimary=false, &$outputXML=null) {
		$outputXML = ($outputXML===null)?$this->xml:$outputXML;
		if ($module === null) {
			$module = DEFAULT_MODULE;
			if ($isPrimary) $this->primaryModule['module'] = $module;
		}
		if ($method === null) {
			$method = "run";
			if ($isPrimary) $this->primaryModule['method'] = $method;
		}
		if (class_exists($module)) {
			$mod = new $module($this);
			if (!is_callable(array($mod,$method))) {
				if ($isPrimary) {
					$this->urlParams = array_merge(array($method), $this->urlParams);
					$this->primaryModule['method'] = "run";
				}
				$method = "run";
			}
			if ($this->user->can($module, $method)) {
				$outputXML->$module->$method = "";
				$mod->xml = $outputXML->$module->$method->children();
				call_user_func_array(array(&$mod, $method), $params); // $mod->$method();
				if ($isPrimary) {
					$this->addTemplate(FRAMES_PATH.$mod->frame.".xsl");
					$this->parseTemplate(FRAMES_PATH.$mod->frame.".xsl");
					
					$outputXML['primaryModule'] = $module;
					$outputXML['primaryModuleMethod'] = $method;
				}
				$this->addTemplate(MODULES_TEMPLATES_PATH.$module."/".$method.".xsl");
			} else {
				if ($isPrimary) $this->user->authorizationRequired();
			}
		} else {
			throw new Exception("module not found", 404);
		}
	}
	
	public function parseTemplate ($templatePath) {
		if (XSL_FRAMES_CACHE) {
			$cacheFileName = ROOT_PATH.'cache/'.md5($templatePath).'.cache';
			if (is_file($cacheFileName)) {
				$cachedModules = unserialize(file_get_contents($cacheFileName));
				foreach ($cachedModules as $m) {
					$this->moduleRun($m['module'], $m['method'], $m['params']);
				}
				return;
			}
		}
		$modules = array();
		$templateDoc = new DOMDocument();
		$templateDoc->load($templatePath);
		$xp = new DOMXPath($templateDoc);
		foreach ($xp->query("//xsl:apply-templates[@select]") as $callTemplate) {
			$explode = explode("/", $callTemplate->getAttribute('select'));
			$module = $explode[0];
			$method = (isset($explode[1]) and $explode[1]!='')?$explode[1]:null;
			$params = explode("|", $callTemplate->getAttribute('params'));
			if (!empty($module) and preg_match('/^[0-9A-Z_]{2,30}$/is',$module) and class_exists($module)) {
				if (is_subclass_of($module, "module")) {
					$this->moduleRun($module, $method, $params);
					$modules[] = array('module'=>$module,'method'=>$method,'params'=>$params);
				}
			}
		}
		if (XSL_FRAMES_CACHE) {
			file_put_contents($cacheFileName, serialize($modules));
		}
	}
	
	private function output() {
		$templatesList = $this->getTemplatesList(true);
		if (XSL_CACHE) {
			$cacheFileName = ROOT_PATH.'cache/'.md5(implode(null, $templatesList).LOCALE).'.xsl';
			if (is_file($cacheFileName)) {
				$baseXSL = new DOMDocument();
				$baseXSL->load($cacheFileName);
			} else {
				$baseXSL = $this->buildCompleteXSL();
				file_put_contents($cacheFileName, $baseXSL->saveXML());
			}
		} else {
			$baseXSL = $this->buildCompleteXSL();
		}

		if (OUTPUT == "HTML") {
			$xsl = new XSLTProcessor();
			$xsl->importStyleSheet($baseXSL);
			header('Content-type: text/html');
			echo $xsl->transformToXML($this->xml);
//			echo $xsl->transformToDoc($this->xml)->saveHTML();
		} elseif (OUTPUT == "XML") {
			header('Content-type: text/xml');
			echo $this->xml->asXML();
		} elseif (OUTPUT == "XSL") {
			header('Content-type: text/xml');
			echo $baseXSL->saveXML();
		}
	}
	
	private function buildCompleteXSL() {
		$baseXSL = new DOMDocument();
		$baseXSL->load(ROOT_PATH."core/base.xsl");
		$xp = new DOMXPath($baseXSL);
		$stylesheetNode = $xp->query("/xsl:stylesheet")->item(0);
		
		foreach ($this->getTemplatesList() as $template) {
			if (is_file($template)) {
				if ($templateContent = file_get_contents($template)) {
					$templateContent = $this->translateTemplate($templateContent);
					$templateDoc = new DOMDocument();
					if ($templateDoc->loadXML($templateContent)) {
						$xp = new DOMXPath($templateDoc);
						foreach ($xp->query("//xsl:template") as $moduleTemplate) {
							$moduleTemplateNode = $baseXSL->importNode($moduleTemplate, true);
							$stylesheetNode->appendChild($moduleTemplateNode);	
						}
					}
				}
			}
		}
		
		return $baseXSL;
	}
	
	public function translateTemplate($templateContent) {
		preg_match_all('/<gt>(.*?)<\/gt>/is', $templateContent, $pregOut);
		foreach ($pregOut[1] as $word) {
			$templateContent = str_replace('<gt>'.$word.'</gt>', _($word), $templateContent);
		}
		
		preg_match_all('/\[gt\]([^<>]*?)\[\/gt\]/is', $templateContent, $pregOut);
		foreach ($pregOut[1] as $word) {
			$templateContent = str_replace('[gt]'.$word.'[/gt]', _($word), $templateContent);
		}

		return $templateContent;
	}
	
	public function addTemplate($templatePath) {
		$this->templatesList[] = $templatePath;
	}
	
	public function getTemplatesList($sort=false) {
		if ($sort) {
			$this->templatesList = array_unique($this->templatesList);
			asort($this->templatesList);
		}
		return $this->templatesList;
	}
	
	public function getBaseURL($finalSlash=true) {
		$url = "/".$this->primaryModule['module'];
		$url .= (($this->primaryModule['method']!="run")?"/".$this->primaryModule['method']:"");
		$url .= (($finalSlash)?"/":"");
		
		if ($this->urlAlias) {
			$url = $this->urlAlias.substr($url, strlen($GLOBALS['urlAliases']['partial'][$this->urlAlias]));
		}
		
		return $url;
	}
	
	public function jump($url) {
		header("Location: ".$url);
		$this->_die();
	}
	
	public function _die() {
		exit();
	}
	
	public function getRolesList() {
		$constants = get_defined_constants(true);
		foreach ($constants['user'] as $k => $v) {
			if (strpos($k,'ROLE_') === 0)
				$roles[$v] = _($v);
		}
		return $roles;
	}
	
	public function logError($errorMessage) {
		//echo $errorMessage;
	}
}
//******************************************************************
class extendedSimpleXML extends SimpleXMLElement {
	public function writeArray($containerName, $array, $keyAsAttribute=false, $firstLevelTagName="row", $secondLevelTagName="cell") {
		if (!is_array($array)) return false;
		
		if ($containerName === null) {
			$container = $this;
		} else {
			$this->$containerName = "";
			$container = $this->$containerName;
		}
		
		if (is_array(current($array))) {
			$i = count($container->{$firstLevelTagName});
			foreach ($array as $k => $v) {
				$container->{$firstLevelTagName}[$i] = "";
				if ($keyAsAttribute) {
					foreach ($v as $k2 => $v2) $container->{$firstLevelTagName}[$i][$k2] = (string)$v2;
				} else {
					$j = 0;
					foreach ($v as $k2 => $v2) {
						$container->{$firstLevelTagName}[$i]->{$secondLevelTagName}[$j] = (string)$v2;
						$container->{$firstLevelTagName}[$i]->{$secondLevelTagName}[$j]['key'] = $k2;
						$j++;
					}
				}
				$i++;
			}
		} else {
			$i = count($container->{$firstLevelTagName});
			if ($keyAsAttribute) {
				foreach ($array as $k => $v) $container->{$firstLevelTagName}[$i][(string)$k] = (string)$v;
			} else {
				foreach ($array as $k => $v) {
					$container->{$firstLevelTagName}[$i] = (string)$v;
					$container->{$firstLevelTagName}[$i]['key'] = $k;
					$i++;
				}
			}
		}
	}
}
//******************************************************************
class module {
	public $engine = null;
	public $sql = null;
	public $user = null;
	public $title = "Module description";
	public $frame = DEFAULT_FRAME;
	public $permissions = array('*'=>array('*'));
	public $xml = null;
	
	public function __construct($engine) {
		$this->engine = $engine;
		$this->sql = $this->engine->sql;
		$this->user = $this->engine->user;
	}
	
	public function run() { }
}
//******************************************************************
?>